home *** CD-ROM | disk | FTP | other *** search
/ Workbench Add-On / Workbench Add-On - Volume 1.iso / BBS-Archive / Comm / AmiTCP30b2.lha / src / amitcp / kern / amiga_config.c < prev    next >
C/C++ Source or Header  |  1993-12-20  |  17KB  |  664 lines

  1. RCS_ID_C="$Id: amiga_config.c,v 1.21 1993/12/20 18:04:11 jraja Exp $";
  2. /* 
  3.  * Copyright © 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
  4.  *                  Helsinki University of Technology, Finland.
  5.  *                  All rights reserved.
  6.  *
  7.  * amiga_config.c --- Configuring AmiTCP/IP
  8.  *
  9.  * Created      : Thu Apr 22 12:27:04 1993 ppessi
  10.  * Last modified: Mon Dec 20 10:36:28 1993 jraja
  11.  *
  12.  * $Log: amiga_config.c,v $
  13.  * Revision 1.21  1993/12/20  18:04:11  jraja
  14.  * Added missing semicolon + const to parsefile()s first argument.
  15.  *
  16.  * Revision 1.20  1993/12/20  08:41:22  jraja
  17.  * Added external declaration for AmiTCP_Task.
  18.  *
  19.  * Revision 1.19  1993/12/20  08:25:57  jraja
  20.  * Changed rexx function parsing to use function table instead of switch.
  21.  * Added handling for the new "KILL" keyword (sendbreak()).
  22.  *
  23.  * Revision 1.18  1993/11/06  23:51:22  ppessi
  24.  * Changed sprintf() to csprintf()
  25.  *
  26.  * Revision 1.17  1993/06/04  11:16:15  jraja
  27.  * Fixes for first public release.
  28.  *
  29.  * Revision 1.16  1993/06/03  16:38:23  jraja
  30.  * Added VAR_ENUM variable type.
  31.  *
  32.  * Revision 1.15  1993/05/31  23:27:31  jraja
  33.  * Removed Key_CONN and the associated getsockets() call.
  34.  *
  35.  * Revision 1.14  1993/05/29  21:10:15  jraja
  36.  * Added ERR_NOWRITE error, changed return values to symbolic RETURN_XXXX,
  37.  * added check for setting only configuration-writeable variables,
  38.  * cleared return value after SET, Added error reporting to config-file
  39.  * parsing, now just passes by syntax and parse-errors,
  40.  * writes template if command line cannot be parsed.
  41.  *
  42.  * Revision 1.13  1993/05/17  01:07:47  ppessi
  43.  * Changed RCS version.
  44.  *
  45.  * Revision 1.12  1993/05/16  00:13:34  jraja
  46.  * Changed one return value from 20 to RETURN_FAIL.
  47.  *
  48.  * Revision 1.11  93/05/14  11:35:40  11:35:40  ppessi (Pekka Pessi)
  49.  * Cleaned up variables. All netdb special stuff is moved to the amiga_netdb.c.
  50.  * 
  51.  * Revision 1.10  93/05/05  16:09:45  16:09:45  puhuri (Markus Peuhkuri)
  52.  * Fixes for final demo.
  53.  * 
  54.  * Revision 1.9  93/05/04  12:18:00  12:18:00  jraja (Jarno Tapio Rajahalme)
  55.  * fix.
  56.  * 
  57.  * Revision 1.8  93/05/02  18:01:44  18:01:44  jraja (Jarno Tapio Rajahalme)
  58.  * Fixed assignment to the RDA_Source.
  59.  * 
  60.  * Revision 1.7  93/05/02  17:33:43  17:33:43  jraja (Jarno Tapio Rajahalme)
  61.  * Made some of the parseroute.
  62.  * 
  63.  * Revision 1.6  93/04/28  21:54:02  21:54:02  ppessi (Pekka Pessi)
  64.  * Uses now automatically generated variable table.
  65.  * 
  66.  * Revision 1.5  93/04/26  20:33:47  20:33:47  puhuri (Markus Peuhkuri)
  67.  * Add configuration for logfilename and consolename.
  68.  * 
  69.  * Revision 1.4  93/04/24  22:05:59  22:05:59  jraja (Jarno Tapio Rajahalme)
  70.  * Added mbuf statistics.
  71.  * 
  72.  * Revision 1.3  93/04/23  21:01:01  21:01:01  puhuri (Markus Peuhkuri)
  73.  * Updated call to getsockets
  74.  * 
  75.  * Revision 1.2  93/04/23  02:25:53  02:25:53  ppessi (Pekka Pessi)
  76.  * Added logging message amount an length into config variables..
  77.  * 
  78.  * Revision 1.1  93/04/23  00:26:19  00:26:19  ppessi (Pekka Pessi)
  79.  * Initial revision
  80.  */
  81.  
  82. #include <conf.h>
  83.  
  84. #include <sys/param.h>
  85. #include <sys/systm.h>
  86. #include <sys/syslog.h>
  87. #include <sys/socket.h>
  88. #include <sys/malloc.h>
  89. #include <sys/mbuf.h>
  90.  
  91. #include <netdb.h>        /* pathnames */
  92.  
  93. #include <kern/amiga_includes.h>
  94. #include <kern/amiga_config.h>
  95. #include <kern/amiga_netdb.h>
  96. #include <utility/tagitem.h>
  97. #include <dos/rdargs.h>
  98.  
  99. #if __SASC
  100. #include <proto/dos.h>
  101. #elif __GNUC__
  102. #include <inline/dos.h>
  103. #endif
  104.  
  105. #include <net/route.h>
  106. #include <netinet/in_systm.h>
  107. #include <netinet/in.h>
  108. #include <netinet/ip.h>
  109. #include <netinet/in_pcb.h>
  110. #include <netinet/ip_var.h>
  111. #include <netinet/ip_icmp.h>
  112. #include <netinet/icmp_var.h>
  113. #include <netinet/tcp.h>
  114. #include <netinet/tcp_timer.h>
  115. #include <netinet/tcp_var.h>
  116. #include <netinet/udp.h>
  117. #include <netinet/udp_var.h>
  118.  
  119. /* External functions */
  120. int inet_aton(register const char *cp, struct in_addr *addr);
  121. int ultoa(unsigned long ul,char *buffer);
  122.  
  123. extern struct Task *AmiTCP_Task; /* referenced by sendbreak() */
  124.  
  125. /* Parsing error messages */
  126. UBYTE ERR_UNKNOWN[]     = "Unknown command\n";
  127. UBYTE ERR_ILLEGAL_VAR[] = "%s: unknown variable %s\n";
  128. UBYTE ERR_ILLEGAL_IND[] = "%s: unknown index %s\n";
  129. UBYTE ERR_SYNTAX[]      = "Syntax error\n";
  130. UBYTE ERR_TOO_LONG[]    = "Result too long\n";
  131. UBYTE ERR_MEMORY[]      = "Memory exhausted\n";
  132. UBYTE ERR_NONETDB[]     = "No active net database\n";
  133. UBYTE ERR_VALUE[]       = "Illegal value\n";
  134. UBYTE ERR_NOWRITE[]     = "%s: Variable %s is not writeable\n";
  135.  
  136. /* Array of parsing functions. Note that the order is same as in the
  137.  * enum keyword.
  138.  */
  139. var_f rexx_parse_funs[] = {
  140.   getvalue,            /* KEY_QUERY */
  141.   setvalue,            /* KEY_SET */
  142.   readfile,            /* KEY_READ */
  143.   parseroute,            /* KEY_ROUTE */
  144.   do_netdb,            /* KEY_ADD */
  145.   reset_netdb,            /* KEY_RESET */
  146.   sendbreak            /* KEY_KILL */
  147.   };
  148.  
  149. /*
  150.  * Parse a Rexx command line
  151.  */
  152. LONG
  153. parseline(struct CSource *csarg, UBYTE **errstrp, struct CSource *res)
  154. {
  155.   UBYTE Buffer[KEYWORDLEN];
  156.   enum keyword keyword;
  157.  
  158.   /* Parse the command keyword */
  159.   LONG item = ReadItem(Buffer, sizeof(Buffer), csarg);
  160.  
  161.   if (item == 0)
  162.     return RETURN_OK;
  163.   else if (item < 0) {
  164.     *errstrp = ERR_SYNTAX;
  165.     return RETURN_WARN;
  166.   }
  167.   
  168.   if ((keyword = FindArg((UBYTE*)REXXKEYWORDS, Buffer)) < 0) {
  169.     *errstrp = ERR_UNKNOWN;
  170.     return RETURN_WARN;
  171.   }
  172.  
  173.   return rexx_parse_funs[keyword](csarg, errstrp, res);
  174. }
  175.  
  176. /* 
  177.  * 'Parse' the "KILL" command
  178.  */
  179. LONG 
  180. sendbreak(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  181. {
  182.   Signal(AmiTCP_Task, SIGBREAKF_CTRL_C);
  183.   return RETURN_OK;
  184. }
  185.  
  186. extern UBYTE *KW_VARS;
  187. extern struct cfg_variable variables[];
  188.  
  189. /* 
  190.  * Parse the "Query" commands
  191.  */
  192. LONG 
  193. getvalue(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  194. {
  195.   UBYTE Buffer[KEYWORDLEN];
  196.   WORD var, index;
  197.   LONG vlen;
  198.   UBYTE *value = NULL;
  199.  
  200.   Buffer[0] = '\0';
  201.  
  202.   while ((var = ReadItem(Buffer, sizeof(Buffer), args)) > 0) {
  203.     if ((var = FindArg(KW_VARS, Buffer)) < 0 ||
  204.     (variables[var].flags & VF_READ) != VF_READ) { 
  205.       res->CS_CurChr = 0;
  206.       csprintf(res, ERR_ILLEGAL_VAR, "getvalue", Buffer);
  207.       *errstrp = res->CS_Buffer;
  208.       return RETURN_WARN;
  209.     } else {
  210.  
  211.       if (variables[var].flags & VF_TABLE) {
  212.     if (ReadItem(Buffer, sizeof(Buffer), args) <= 0 ||
  213.        (index = FindArg((UBYTE *)variables[var].index, Buffer)) < 0) {
  214.       res->CS_CurChr = 0;
  215.       csprintf(res, ERR_ILLEGAL_IND, "getvalue", Buffer);
  216.       *errstrp = res->CS_Buffer;
  217.       return RETURN_WARN;
  218.     } 
  219.       } else {
  220.     index = 0;
  221.       }
  222.  
  223.       switch (variables[var].type) {
  224.       case VAR_FUNC:
  225.     if (variables[var].value) {
  226.       if (vlen = (*(var_f)(variables[var].value))(args, errstrp, res)) 
  227.         return vlen;
  228.     } else {
  229.       *errstrp = ERR_ILLEGAL_VAR;
  230.       return RETURN_ERROR;
  231.     }
  232.     value = (char *)1; /* successful flag.. */
  233.     continue; /* while() */
  234.       case VAR_LONG:
  235.       case VAR_FLAG:
  236.     vlen = ultoa(((LONG*)variables[var].value)[index], Buffer);
  237.     value = Buffer;
  238.     break;
  239.       case VAR_STRP:
  240.     value = ((UBYTE **)variables[var].value)[index];
  241.     vlen  = strlen(value);
  242.     break;
  243.       case VAR_INET:
  244.     { 
  245.       ULONG s_addr = 
  246.         ((struct in_addr *)variables[var].value)[index].s_addr;
  247.       vlen = sprintf(Buffer, "%ld.%ld.%ld.%ld",       
  248.              (s_addr>>24) & 0xff, (s_addr>>16) & 0xff, 
  249.              (s_addr>>8) & 0xff, s_addr & 0xff);
  250.       value = Buffer;
  251.     }
  252.     break;
  253.       case VAR_ENUM:
  254.     {
  255.       ULONG i = 0, nth;
  256.       STRPTR p;
  257.  
  258.       nth = ((ULONG*)variables[var].value)[index];
  259.       /*
  260.        * search nth keyword from the template
  261.        */
  262.       value = (STRPTR)variables[var].notify;
  263.       while (*value && i < nth)
  264.         if (*value++ == ',')
  265.           i++;
  266.       if (i < nth) {    /* value not found */
  267.         *errstrp = ERR_VALUE;
  268.         return RETURN_ERROR;
  269.       }
  270.       /*
  271.            * find the length of the answer
  272.        */
  273.       p = value;
  274.       while(*p && *p != '=' && *p != ',')
  275.         p++;
  276.  
  277.       vlen = p - value;
  278.     }
  279.     break;
  280.       }
  281.       /* prepend by space? */
  282.       if (res->CS_CurChr) 
  283.     res->CS_Buffer[res->CS_CurChr++] = ' ';
  284.       if (vlen + res->CS_CurChr > res->CS_Length) {
  285.     *errstrp = ERR_TOO_LONG;
  286.     return RETURN_ERROR;
  287.       } 
  288.       bcopy(value, res->CS_Buffer + res->CS_CurChr, (WORD)vlen);
  289.       res->CS_CurChr += vlen;
  290.     }
  291.   }
  292.   if (!value || var != ITEM_NOTHING) {
  293.     *errstrp = ERR_SYNTAX;
  294.     return RETURN_WARN;
  295.   }
  296.   res->CS_Buffer[res->CS_CurChr] = '\0';
  297.   return RETURN_OK;
  298. }
  299.  
  300. /* 
  301.  * Parse the "Set" commands
  302.  *
  303.  * TODO: notifications for VAR_INET;
  304.  */
  305. LONG 
  306. setvalue(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  307. {
  308.   UBYTE Buffer[KEYWORDLEN];
  309.   LONG  BufLen = sizeof(Buffer);
  310.   LONG vlen, item;
  311.   WORD var, index;
  312.   UBYTE *value;
  313.   void *dp = NULL;        /* pointer to data item */
  314.  
  315.   Buffer[0] = '\0';
  316.  
  317.   while ((item = ReadItem(Buffer, BufLen, args)) > 0) {
  318.     if ((var = FindArg(KW_VARS, Buffer)) < 0 ||
  319.     (!(variables[var].flags & VF_WRITE) &&
  320.      (initialized || !(variables[var].flags & VF_CONF)))) { 
  321.       res->CS_CurChr = 0;
  322.       csprintf(res, (var < 0) ? ERR_ILLEGAL_VAR : ERR_NOWRITE, 
  323.            "setvalue", Buffer);
  324.       *errstrp = res->CS_Buffer;
  325.       return RETURN_WARN;
  326.     } else {
  327.       if (variables[var].flags & VF_TABLE) {
  328.         if (ReadItem(Buffer, BufLen, args) <= 0 ||
  329.         (index = FindArg((UBYTE *)variables[var].index, Buffer)) < 0) {
  330.       res->CS_CurChr = 0;
  331.       csprintf(res, ERR_ILLEGAL_IND, "setvalue", Buffer);
  332.       *errstrp = res->CS_Buffer;
  333.       return RETURN_WARN;
  334.     } 
  335.       } else {
  336.     index = 0;
  337.       }
  338.  
  339.       if (variables[var].type != VAR_FLAG &&
  340.       *CURRENT(args) == '=')
  341.     args->CS_CurChr++;
  342.  
  343.       /* If dp is different for a type, 
  344.        * it must be calculated in the case statement 
  345.        */
  346.       dp = (void *)((LONG *)variables[var].value + index);
  347.  
  348.       switch (variables[var].type) {
  349.       case VAR_FUNC:
  350.     if (variables[var].notify) {
  351.       if (vlen = (*(var_f)(variables[var].notify))(args, errstrp, res)) 
  352.         return vlen;
  353.     } else {
  354.       *errstrp = ERR_ILLEGAL_VAR;
  355.       return RETURN_ERROR;
  356.     }
  357.     break;
  358.       case VAR_LONG:
  359.     if ((vlen = StrToLong(CURRENT(args), &item)) <= 0) 
  360.       goto reterr;
  361.     if (variables[var].notify) 
  362.       if (!(*variables[var].notify)(dp, item)) {
  363.         *errstrp = ERR_VALUE;
  364.         return RETURN_WARN;
  365.       } 
  366.     *(LONG*)dp = item;
  367.     args->CS_CurChr += vlen;
  368.     break;
  369.       case VAR_STRP:
  370.     if (ReadItem(Buffer, BufLen, args) <= 0)
  371.       goto reterr;
  372.     vlen  = strlen(Buffer) + 1;
  373.     value = bsd_malloc(vlen, M_CFGVAR, M_WAITOK);
  374.     if (!value) {
  375.       *errstrp = ERR_MEMORY;
  376.       return RETURN_ERROR;
  377.     }
  378.     strcpy(value, Buffer);
  379.     if (variables[var].notify) 
  380.       if (!(*variables[var].notify)(dp, (LONG) value)) {
  381.         bsd_free(value, M_CFGVAR);
  382.         *errstrp = ERR_VALUE;
  383.         return RETURN_WARN;
  384.       }
  385.     if (variables[var].flags & VF_FREE) {
  386.       bsd_free(*(UBYTE **)dp, M_CFGVAR); 
  387.     }
  388.     *(UBYTE **)dp = value;
  389.     variables[var].flags |= VF_FREE;
  390.     break;
  391.       case VAR_INET:
  392.     /* Currently, nameservice can not be used */
  393.     if (ReadItem(Buffer, BufLen, args) <= 0)
  394.       goto reterr;
  395.     if (!inet_aton(Buffer, (struct in_addr *)dp))
  396.       goto reterr;
  397.     break;
  398.       case VAR_ENUM:
  399.     if (ReadItem(Buffer, BufLen, args) <= 0)
  400.       goto reterr;
  401.     /*
  402.      * Match the item against the template. The value is the index of
  403.      * the matching keyword, if one is found.
  404.      */
  405.     if ((vlen = FindArg((STRPTR)variables[var].notify, Buffer)) < 0)
  406.       goto reterr;
  407.     /*
  408.      * Set the new value. Note that there is no notify function, since
  409.      * the notify field was used for the template.
  410.      */
  411.     *(LONG *)dp = vlen;
  412.     break;
  413.       }
  414.     }
  415.   }
  416.  
  417.   if (item != ITEM_NOTHING) {
  418.   reterr:      
  419.     *errstrp = ERR_SYNTAX;
  420.     return RETURN_WARN;
  421.   }
  422.  
  423. #define DONE "Done."
  424.   bcopy(DONE, res->CS_Buffer + res->CS_CurChr, sizeof(DONE));
  425.   res->CS_CurChr += sizeof(DONE);
  426.   res->CS_Buffer[res->CS_CurChr] = '\0';
  427.   return RETURN_OK;
  428. }
  429.  
  430. /*
  431.  * SET WITH a file
  432.  */
  433. LONG read_sets(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  434. {
  435.   UBYTE * buf = res->CS_Buffer;
  436.  
  437.   if (ReadItem(buf, res->CS_Length, args) < 0) {
  438.     *errstrp = ERR_SYNTAX;
  439.     return RETURN_WARN;
  440.   } else {
  441.     return parsefile(buf, errstrp, res);
  442.   }
  443. }
  444.  
  445. /*
  446.  * Parse a 'WITH' command
  447.  */
  448. LONG 
  449. readfile(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  450. {
  451.   *errstrp = "readfile() is currently unimplemented\n";
  452.   return RETURN_WARN;
  453. }
  454.  
  455. #define CMDLINETEMP "WITH/K,NOO=NOCONFIG/S,DEBUG/S"
  456.  
  457. #define CL_WITH   0
  458. #define CL_NOCFG  1
  459. #define CL_DEBUG  2        /* Currently default, does nothing */
  460. #define CL_SIZE   3
  461.  
  462. /*
  463.  * Read command file 
  464.  */
  465. LONG 
  466. parsefile(UBYTE const *name, UBYTE **errstrp, struct CSource *res)
  467. {
  468.   LONG retval = RETURN_OK, ioerr = 0;
  469.   struct CSource arg;
  470.   int line = 0;
  471.   BPTR fh;
  472.   UBYTE *buf = AllocMem(CONFIGLINELEN, MEMF_PUBLIC);
  473.  
  474.   if (buf) {
  475.     arg.CS_Buffer = buf;
  476.     if (fh = Open(name, MODE_OLDFILE)) {
  477.       while (FGets(fh, buf, CONFIGLINELEN)) {
  478.     line++;
  479.     if (*buf == '#')
  480.       continue;
  481.     arg.CS_Length = strlen(buf);
  482.     arg.CS_CurChr = 0;
  483.     retval = setvalue(&arg, errstrp, res);
  484.  
  485.     if (retval == RETURN_OK)
  486.       continue;
  487.     if (retval != RETURN_WARN) /* severe error */
  488.       break;
  489.     
  490.     /* Print the error to the "stdout" */
  491.     Printf("%s: line %ld, col %ld: %s",
  492.            name, line, arg.CS_CurChr, *errstrp);
  493.     retval = RETURN_OK;
  494.       }
  495.       /* Check file error */ 
  496.       ioerr = IoErr();
  497.  
  498.       Close(fh);
  499.     } else {
  500.       ioerr = IoErr();
  501.     }
  502.  
  503.     if (ioerr) {
  504.       Fault(ioerr, name, res->CS_Buffer, res->CS_Length);
  505.       *errstrp = res->CS_Buffer;
  506.       retval = RETURN_ERROR;
  507.     }
  508.     FreeMem(buf, CONFIGLINELEN);
  509.   } else {
  510.     *errstrp = ERR_MEMORY;
  511.     retval = RETURN_FAIL;
  512.   }
  513.  
  514.   return retval;
  515. }
  516.  
  517. /* 
  518.  * Read command line arguments and configuration file
  519.  */ 
  520. BOOL
  521. readconfig(void)
  522. {
  523.   UBYTE result[REPLYBUFLEN + 1]; /* for error returns */
  524.   struct CSource res;
  525.   struct RDArgs *rdargs = NULL;
  526.   LONG args[CL_SIZE] = { 0 };
  527.   UBYTE *errstr;
  528.   LONG error = 0;
  529.  
  530.   res.CS_Buffer = result;      
  531.   res.CS_Length = sizeof(result); 
  532.   res.CS_CurChr = 0;
  533.  
  534.   /* Parse command line arguments, if any */
  535.   rdargs = ReadArgs(CMDLINETEMP, args, NULL);
  536.  
  537.   if (!rdargs) {
  538.     Printf("Argument error. Template: %s\n", CMDLINETEMP);
  539.     return FALSE;
  540.   }
  541.  
  542.   if (!args[CL_NOCFG]) 
  543.     /* Read default configuration file */
  544.     error = parsefile(_PATH_AMITCP_CONFIG, &errstr, &res);
  545.  
  546.   if (!error && args[CL_WITH])
  547.     /* Read given file */
  548.     error = parsefile((STRPTR)args[CL_WITH], &errstr, &res);
  549.   
  550.   FreeArgs(rdargs);
  551.  
  552.   if (error)
  553.     Printf("AmiTCP/IP Configuration: %s\n", errstr);
  554.  
  555.   return ((BOOL)!error);
  556. }
  557.  
  558. #if 0
  559. /*
  560.  * The order of following keywords is selected to reflect the order of
  561.  * route message numbers in <net/route.h>, so DO NOT CHANGE THE ORDER.
  562.  */
  563. static STRPTR KW_ROUTE_CMDS = 
  564.   "RESET,ADD,DELETE,CHANGE,GET";
  565.  
  566. static STRPTR ROUTE_TEMPLATE =
  567.   "NET/S,HOST/S,DESTINATION/A,GATEWAY/A,INTERFACE/S,HCNT=HOPCOUNT/K/N";
  568.  
  569. enum route_template
  570. { ROUTE_NET, ROUTE_HOST, ROUTE_DESTINATION, ROUTE_GATEWAY, ROUTE_INTERFACE,
  571.   ROUTE_HOPCOUNT, ROUTE_TEMPLATE_SIZE };
  572.  
  573. #endif /* 0 */
  574.  
  575. /*
  576.  * Parse route command
  577.  */
  578. LONG 
  579. parseroute(struct CSource *args, UBYTE **errstrp, struct CSource *res)
  580. {
  581. #if 1
  582.   *errstrp = "ROUTE not implemented.\n";
  583.   return RETURN_FAIL;
  584. #else
  585.   UBYTE Buffer[KEYWORDLEN];
  586.   LONG  BufLen = sizeof(Buffer);
  587.   struct RDArgs *rdargs;
  588.   LONG argArray[ROUTE_TEMPLATE_SIZE] = { 0 };
  589.   LONG vlen, item;
  590.   WORD index;
  591.   UBYTE *value;
  592.   void *dp = NULL;        /* pointer to data item */
  593.   int req, flags = 0;
  594.   struct sockaddr_in destination, gateway, netmask;
  595.   int error;
  596.  
  597.   Buffer[0] = '\0';
  598.  
  599.   if ((item = ReadItem(Buffer, BufLen, args)) <= 0 
  600.       || (req = FindArg(KW_ROUTE_CMDS, Buffer)) < 0) {
  601.     *errstrp = ERR_SYNTAX;
  602.     return RETURN_FAIL;
  603.   }
  604.  
  605.   if (req == 0)    {        /* RESET, delete all routes */
  606.     *errstrp = "route reset is currently unimplemented\n";
  607.     return RETURN_FAIL;
  608.   }
  609.  
  610.   if (req == RTM_CHANGE) {
  611.     *errstrp = "route change is currently unimplemented\n";
  612.     return RETURN_FAIL;
  613.   }
  614.   if (req == RTM_GET) {
  615.     *errstrp = "route get is currently unimplemented\n";
  616.     return RETURN_FAIL;
  617.   }
  618.   
  619.   /*
  620.    * Initialize the RDArgs structure for ReadArgs()
  621.    */
  622.   rdargs = AllocDosObjectTags(DOS_RDARGS, TAG_END);
  623.   if (rdargs == NULL) {
  624.     *errstrp = ERR_MEMORY;
  625.     return RETURN_FAIL;
  626.   }
  627.  
  628.   rdargs->RDA_Source = *args;
  629.   rdargs->RDA_DAList = NULL;
  630.   rdargs->RDA_Buffer = NULL;
  631.   rdargs->RDA_BufSiz = 0;
  632.   rdargs->RDA_ExtHelp = NULL;
  633.   rdargs->RDA_Flags = 0;
  634.  
  635.   if (ReadArgs(ROUTE_TEMPLATE, argArray, rdargs) == NULL
  636.       || (argArray[ROUTE_HOST] && argArray[ROUTE_NET])) {
  637.     FreeDosObject(DOS_RDARGS, rdargs);
  638.     *errstrp = ERR_SYNTAX;
  639.     return RETURN_FAIL;
  640.   }
  641.   
  642.   /*
  643.    * Find destination host/network
  644.    */
  645.   /* INCOMPLETE */
  646.  
  647.   if (!argArray[ROUTE_INTERFACE])
  648.     flags |= RTF_GATEWAY;
  649.   if (argArray[ROUTE_HOST])
  650.     flags |= RTF_HOST;
  651.  
  652. #if 0
  653.   error = rtrequest(req, &destination, &gateway,
  654.             (flags & RTF_HOST) ? NULL : &netmask,
  655.             flags, (struct rtentry **)0);
  656. #endif
  657.  
  658.   FreeArgs(rdargs);
  659.   FreeDosObject(DOS_RDARGS, rdargs);
  660.  
  661.   return RETURN_OK;
  662. #endif
  663. }
  664.